﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Collections.ObjectModel;
using System.Threading;

namespace RecommendatioNetwork
{
   
    [Serializable()]
    public class Repository
    {

        #region Proprietà

        private Configuration config;
        public Configuration Configuration {
            get { return config; }
            set { config = value; }
        }
        
        //contiene l'id univoco del peer
        private string peer=Guid.NewGuid().ToString();

        //Contiente la lista dei miei commenti
        private List<Comment> comments=new List<Comment>();
        
        //Contiene la lista degli Uri che conosco, associato con il proprio Rankin
        private Dictionary<Uri,Ranking> uris=new Dictionary<Uri,Ranking>();

        //Contiene la lista delle ricerche attive
        private Dictionary<Search,List<Comment>> activeSearch=new Dictionary<Search,List<Comment>>();

        //responseCommentsSended conserva gli ultimi Commenti inviati da un particolare Peer
        //lo scopo è di non inviare più volte la Response alla stessa Query.
        //SizeMaxOfResponseCommentsSended conserva il valore massimo che lista puo' contentere
        //Ogni stringa appartenente alla lista contiene una stringa univoca "UriRichiedente"+"TimestampDellaRichesta"
        private Queue<string> responseCommentsSended;

        //coda delle risposte che poi andranno nello spooler
        private Queue<Packet> queueResponse=new Queue<Packet>();

        

        //proprietà Ipserver
        public string IPserver
        {
            get;
            set;
        }
        //proprietà mio Uri
        public Uri MyUri
        {
            get {
            UriBuilder ub = new UriBuilder(Uri.UriSchemeHttp, IPserver, Configuration.Port, Configuration.ServiceName);
            return ub.Uri; 
            }
            
        }


        //GET DEL MIO PEER
        public string MyPeer {
            get { return peer; }
        }

        //GET DELLA LISTA DEGLI URI
        public Dictionary<Uri,Ranking> UrisRank
        {
            get {return uris; }
            set { uris = value;}
        }

        

        // GET DELLA LISTA DEI MIEI COMMENTI
        public List<Comment>  MyComments
        {
            get{return comments;}
        }

        /// <summary>
        ///  Proprietà che conserva la cache degli ultime risposte a ricerca
        /// </summary>
        public Queue<string> ResponseCommentsSended
        {
            get { return responseCommentsSended; }
        }

        public Queue<Packet> QueueResponse
        {
            get { return queueResponse; }
        }


        //METODO GET DELLA LISTA DI ACTIVE SEARCH
        public Dictionary<Search,List<Comment>> ActiveSearch
        {
            get { return activeSearch; }
            set { activeSearch = value; }
        }

        #endregion

        #region Costruttore
        public Repository(Model model)
        {
            this.config = model.MyConfiguration;
            responseCommentsSended=new Queue<string>(System.Convert.ToInt32(Configuration.DimCacheServedPeer));
            SetIpServer();
           
        }
        #endregion


        //Verifico che la lista degli uri amici non sia piena
        //In caso lo fosse, questo metodo elimina l'Uri con il Ranking minore.
        //Torna true se ha eliminato l'ultimo Uri in base al rank, altrimenti false.
        public void CheckListOfNeighborIsNotFull()
        {
         
           while (UrisRank.Count > this.Configuration.MaxNumberOfNeighbor)
            {             
                foreach (KeyValuePair<Uri, Ranking> kvp in UrisRank)
                {
                    //implementazione comparable min nella classe Ranking
                    if (kvp.Value.Rank == UrisRank.Values.Min().Rank)
                    {
                        UrisRank.Remove(kvp.Key);
                        break;
                    }
                }
            }
           
        }
        public void AddResponseSearch(Packet p) {
            
            if (UrisRank.ContainsKey(p.UriToSend)) {UrisRank[p.UriToSend].Response++; }
            else UrisRank.Add(p.UriToSend, new Ranking(Configuration) { Request = 1, Response = 1, RequestFailed = 0 });
            OnNeighborsChanged(EventArgs.Empty);
            //Aggiungo il commento
            try
            { 
                foreach (KeyValuePair<Search,List<Comment>> kvp in ActiveSearch)
                {
                    if (string.Equals(kvp.Key.KeyRequest, p.Search.KeyRequest, StringComparison.InvariantCultureIgnoreCase))
                    {
                        
                        if (kvp.Value.Contains(p.Comment))
                        { 
                            int i = kvp.Value.IndexOf(p.Comment);
                            int j = kvp.Value[i].ReplyResponse;
                            kvp.Value.Remove(p.Comment);
                            p.Comment.ReplyResponse=j+1;
                            kvp.Value.Add(p.Comment);
                            OnActiveSearchChanged(p, EventArgs.Empty);
                            return;
                        }
                        else
                        {
                            p.Comment.ReplyResponse = 1;
                            kvp.Value.Add(p.Comment);
                            OnActiveSearchChanged(p, EventArgs.Empty);
                            return;
                        }
                    }    
                }
            }catch (Exception ee) { Console.WriteLine(ee.ToString()); }
        }
        public bool CheckResponseAlreadySent(string query)
        {
            if (ResponseCommentsSended.Contains(query)) { return false; }
            ResponseCommentsSended.Enqueue(query);
            return true;
        }
        //Verifico se la richiesta non è scaduta
        public bool CheckRequestNotExpiry(DateTime dateOfExpiry)
        {
            int compare = DateTime.Compare(DateTime.UtcNow, dateOfExpiry);
            //se compare è <0 vuol dire che la prima data è precedente alla seconda
            if (compare < 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public void SetIpServer() {
            foreach (System.Net.IPAddress IP in System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName()))
            {
                if (IP.ToString().Contains("."))
                {
                    IPserver = IP.ToString();
                    break;
                }
            }
        }
        public void AddNeighbors(Search search)
        {
            if (!UrisRank.ContainsKey(search.Uri))
            {
                UrisRank.Add(search.Uri, new Ranking(Configuration) { Request = 1, Response = 1 });
                OnNeighborsChanged(EventArgs.Empty);
                Sender sender = new Sender(this);
                sender.NewRequestAtNewNeighbors(search.Uri);
            }
        }
        public List<Comment> SearchInMyComments(Search search)
        {  
            List<Comment> result = MyComments.FindAll(
                delegate(Comment comment)
                {
                    try
                    {
                        //MessageBox.Show("SearchInMyComments");
                        if (string.Equals(search.Typology, "All Categories", StringComparison.InvariantCultureIgnoreCase) || comment.Typology.Equals(search.Typology))
                        {
                            if (search.TypeOfSearch.Equals("Titolo"))
                                if (comment.NameIndex.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(search.TextSearched.ToLower(System.Globalization.CultureInfo.InvariantCulture)) || search.TextSearched.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(comment.NameIndex.ToLower(System.Globalization.CultureInfo.InvariantCulture)))
                                    return true;
                            if (search.TypeOfSearch.Equals("Titolo e testo"))
                                if (search.TextSearched.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(comment.NameIndex.ToLower(System.Globalization.CultureInfo.InvariantCulture)) || search.TextSearched.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(comment.TextComment.ToLower(System.Globalization.CultureInfo.InvariantCulture)) || comment.TextComment.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(search.TextSearched.ToLower(System.Globalization.CultureInfo.InvariantCulture)))
                                    return true;
                        }
                    }
                    catch (Exception ee) { MessageBox.Show(ee.ToString()); return false; }

                    return false;
                });
            return result;
        }
          

        #region eventi mandati al viewmodel
        [field:NonSerialized]
        public event EventHandler neighborsChanged;
       
        [field: NonSerialized]
        public event EventHandler activeSearchChanged;

        public virtual void OnNeighborsChanged(EventArgs args)
        {    
            if (neighborsChanged != null)
            {
                neighborsChanged(this,EventArgs.Empty); // attiva l'evento neighborsChanged		
            }
        }
        protected virtual void OnActiveSearchChanged(Packet p,EventArgs args)
        {
            if (activeSearchChanged != null)
                activeSearchChanged(this,p); // attiva l'evento activeSearchChanged	
        }
        #endregion

        #region eventi acoltati dal listener
       
        public void ListenerSearch(Object obj, EventArgs x)
        {
            
            Search search = x as Search;
            if (search.TimeToLive != 0 && CheckResponseAlreadySent(search.KeyRequest) && CheckRequestNotExpiry(search.DateOfExpiry))
            {
                
                    Sender sender = new Sender(this);
                    //LOGICA RELATIVA AL REINSTRADAMENTO
                    ThreadStart threadRouting = delegate
                    {
                        sender.Routing(search);
                        //AddNeighbors(search);
                    };
                    Thread t = new Thread(threadRouting);
                    t.Start();

                    //LOGICA RELATIVA ALLA RICERCA INTERNA
                    List<Comment> result = SearchInMyComments(search);
                    if (result.Count != 0)
                    {
                        sender.SendResponse(search, result);
                    }
                    t.Join();
                    AddNeighbors(search);
                    
               
               
            }
        }
        public void AddResponse(Object sender, EventArgs x)
        {
            Packet packet = x as Packet;
            AddResponseSearch(packet);
        }
        
        #endregion

    }


}
